package com.yz.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yz.common.constant.CacheConstants;
import com.yz.common.factory.JedisFactory;
import com.yz.common.properties.JwtProperties;
import com.yz.model.dto.user.UpdatePasswordRequest;
import com.yz.user.mapper.UserMapper;
import com.yz.model.entity.user.User;
import com.yz.user.service.UserService;
import com.yz.common.utils.JwtUtils;
import com.yz.common.utils.MailUtils;
import com.yz.common.utils.VerifyUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import redis.clients.jedis.Jedis;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created With IntelliJ IDEA.
 * Descriptions:
 * author: Mr.Du
 * Date: 2023/12/7
 * Time: 11:10
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserMapper userMapper;

    private final JwtProperties jwtProperties;

    private final MailUtils mailUtils;

    private final JedisFactory jedisFactory;

    private final JavaMailSender javaMailSender;

    @Override
    public String userLogin(String username, String password) {
        if(StringUtils.isAnyBlank(username, password)) {
            throw new RuntimeException("用户名或密码不能为空");
        }
        User user = this.findUserByUserName(username);
        if (user == null){
            throw new RuntimeException("用户不存在");
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(password, user.getPassword());
        if(!matches){
            throw new RuntimeException("密码错误");
        }
        String token = generateToken(user);
        return token;
    }

    @Override
    public String userLoginWithEmail(String email, String code) {
        // 校验邮箱是否存在
        User user = this.verifyEmail(email);
        // 校验邮箱和验证码是否正确
        this.verifyEmailAndCode(email, code);
        String token = this.generateToken(user);
        return token;
    }

    @Override
    public Long userRegister(String username, String password, String email, String code) {
        if(StringUtils.isAnyBlank(username, password, email, code)){
            throw new RuntimeException("参数不能为空");
        }
        User user = this.findUserByUserName(username);
        if(user != null){
            throw new RuntimeException("用户名已存在");
        }
        if(!VerifyUtils.isEmail(email)){
            throw new RuntimeException("邮箱格式错误");
        }
        user = this.findUserByEmail(email);
        if(user != null){
            throw new RuntimeException("邮箱已被注册");
        }
        this.verifyEmailAndCode(email, code);
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encodePasswd = bCryptPasswordEncoder.encode(password);
        user = new User();
        user.setUsername(username);
        user.setNickname(username);
        user.setPassword(encodePasswd);
        user.setEmail(email);
        user.setAmount(100L);
        user.setCountPromotion(5);
        user.setAvatar("https://grouphy.oss-cn-beijing.aliyuncs.com/aa.jpg");
        userMapper.insert(user);

        return user.getId();
    }

    @Override
    public User findUserByUserName(String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        return userMapper.selectOne(queryWrapper);
    }

    @Override
    public User findUserByEmail(String email) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email", email);
        return userMapper.selectOne(queryWrapper);
    }

    @Override
    public void getCode(String email) {
        Jedis jedis = jedisFactory.getJedis();
        try{
            StringBuffer sb = VerifyUtils.randomCode();
            String ok = jedis.setex(CacheConstants.CAPTCHA_CODE_KEY + email, CacheConstants.VERIFY_CODE_EXPIRATION, sb.toString());
            if(!ok.equals("OK")){
                throw new RuntimeException("验证码缓存失败");
            }
            new Thread(()->{
                SimpleMailMessage msg = mailUtils.sendSimpleMsg(email, "你的验证码为：" + sb + "，有效期5分钟，如非本人操作，请忽略此验证码");
                javaMailSender.send(msg);
                log.info("发送验证码成功: {}", sb);
            }).start();
        }finally{
            this.closeJedis(jedis);
        }
    }

    @Override
    public void updatePassword(Long uid, UpdatePasswordRequest updatePasswordRequest) {
        Jedis jedis = jedisFactory.getJedis();
        try{
            String oldPassword = updatePasswordRequest.getOldPassword();
            String password = updatePasswordRequest.getPassword();
            String checkPassword = updatePasswordRequest.getCheckPassword();

            if(StringUtils.isAnyBlank(oldPassword, password, checkPassword)){
                throw new RuntimeException("参数不能为空");
            }
            if(!password.equals(checkPassword)){
                throw new RuntimeException("两次密码不一致");
            }
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            String encodePwd = bCryptPasswordEncoder.encode(password);
            User user = userMapper.selectById(uid);
            if(!bCryptPasswordEncoder.matches(oldPassword, user.getPassword())){
                throw new RuntimeException("旧密码错误");
            }
            user.setPassword(encodePwd);
            int res = userMapper.updateById(user);
            if(res == 0){
                throw new RuntimeException("更新失败");
            }
            this.logout(user.getUsername());
        }finally {
            this.closeJedis(jedis);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void consumeAmount(Long uid, Long amount) {
        if(amount <= 0){
            throw new RuntimeException("金额不能小于等于0");
        }
        User user = baseMapper.selectById(uid);
        if(user.getAmount() < amount){
            log.info("余额不足");
            throw new RuntimeException("余额不足");
        }
        user.setAmount(user.getAmount() - amount);
        int row = baseMapper.updateById(user);
        if (row == 0){
            throw new RuntimeException("更新失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reChargeAmount(Long uid, Long amount) {
        if(amount <= 0){
            throw new RuntimeException("金额不能小于等于0");
        }
        User user = baseMapper.selectById(uid);
        user.setAmount(user.getAmount() + amount);
        int row = baseMapper.updateById(user);
        if (row == 0){
            throw new RuntimeException("更新失败");
        }
    }

    @Override
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }

    @Override
    public boolean updateUserCountPromotion(Long id, Integer countPromotion) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        User user = new User();
        user.setCountPromotion(countPromotion);
        log.info("count_promotion: " + countPromotion);
        int update = baseMapper.update(user, queryWrapper);
        return update > 0;
    }

    @Override
    public void logout(String username) {
        Jedis jedis = getJedis();
        try{
            long del = jedis.del(CacheConstants.LOGIN_USER_KEY + username);
            if(del == 0){
                throw new RuntimeException("退出失败");
            }
        } finally {
            this.closeJedis(jedis);
        }
    }

    @Override
    public void updateCountPromotion(Long uid, Integer countPromotion) {
        User user = new User();
        user.setCountPromotion(countPromotion);
        boolean update = this.update(user, null);
        if(!update){
            throw new RuntimeException("更新失败");
        }
    }

    @Override
    public boolean checkEmailIsCurrent(Long uid, String email) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", uid);
        queryWrapper.eq("email", email);
        User user = baseMapper.selectOne(queryWrapper);
        if(user == null){
            return false;
        }
        return true;
    }

    @Override
    @Scheduled(cron = "0 0 0 1 * ?")
    @Retryable(maxAttempts = 3, backoff = @Backoff(delay = 5000))
    public void scheduleUpdateUserCountPromotion() {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("count_promotion", 5);
        int update = baseMapper.update(null, updateWrapper);
    }

    private Jedis getJedis(){
        return jedisFactory.getJedis();
    }

    private void closeJedis(Jedis jedis) { jedis.close(); }

    private void verifyEmailAndCode(String email, String code) {
        Jedis jedis = this.getJedis();
        try{
            String cache_code = jedis.get(CacheConstants.CAPTCHA_CODE_KEY + email);
            if(StringUtils.isBlank(cache_code)){
                throw new RuntimeException("验证码已过期");
            }
            if(!code.equals(cache_code)){
                throw new RuntimeException("验证码错误");
            }
        }finally {
            this.closeJedis(jedis);
        }

    }

    private User verifyEmail(String email) {
        if(!VerifyUtils.isEmail(email)){
            throw new RuntimeException("邮箱格式不正确");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("email", email);
        User user = userMapper.selectOne(queryWrapper);
        if(user == null){
            throw new RuntimeException("该邮箱未注册");
        }
        return user;
    }

    private String generateToken(User user) {
        Jedis jedis = this.getJedis();
        try{
            Map<String, Object> claims = new HashMap<>();
            claims.put("uid", user.getId().toString());
            claims.put("username", user.getUsername());
            // JWT Token 过期时间
            Date expireDate = DateUtils.addDays(new Date(), CacheConstants.TEST_EXPIRATION_TIME);
            String token = JwtUtils.createToken(jwtProperties.getSecretKey(), null, claims, expireDate);
            if(token == null){
                throw new RuntimeException("登录失败,请联系管理员");
            }
            jedis.setex(CacheConstants.LOGIN_USER_KEY + user.getUsername(), CacheConstants.TEST_EXPIRATION, token);
            return token;
        }finally {
            this.closeJedis(jedis);
        }
    }
}
